package com.example.pluginmanager.util;

import com.example.pluginmanager.entity.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtTokenUtil {

    /*放入jwt荷载体*/
    private static final String CLAIM_KEY_USERNAME = "username";
    private static final String CLAIM_KEY_USERID = "userid";
    private static final String CLAIM_KEY_CREATED = "created";

    /*获取配置文件中自定义的一些值*/
    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private Long expiration;


    /*
    根据用户信息生成token (public 的方法对外开放)
    * */
    public String generateToken(User user){
        /*创建jwt荷载信息*/
        HashMap<String, Object> map = new HashMap<>();
        /*用户名*/
        map.put(CLAIM_KEY_USERNAME, user.getUsername());
        /*用户id*/
        map.put(CLAIM_KEY_USERID, user.getId());
        /*创建时间*/
        map.put(CLAIM_KEY_CREATED, new Date());

        return String.join(" ","Bearer",generateToken(map));
    }

    /*
    * 真正生成token的方法,private的
    * 根据荷载来生成token
    * */
    private String generateToken(Map<String,Object> claimMap){
        return Jwts.builder()
                    .setClaims(claimMap)
                    .setExpiration(genetateTokenExpiretion())//设置token有效期
                    .signWith(SignatureAlgorithm.HS256,secret)//使用指定算法,和自定义的盐来加密
                    .compact();
    }

    /*token有效时间*/
    private Date genetateTokenExpiretion(){
        return new Date(System.currentTimeMillis()+expiration*1000);
    }


    /*从token中获取 荷载体 中的信息 (例如 用户名....)
    * 始终会先解析token,当解析失败就会抛出UnsupportedJwtException异常
    * */
    public String getUserNameFromToken(String token){
        String username=null;
        try {
            Claims claims = getClaimsFromToken(token);
            //username = claims.getSubject();//返回荷载中 key = "sub" 的value , 我们在荷载中存的用户名的key就是 "sub"
            username = claims.get(CLAIM_KEY_USERNAME, String.class);
        }catch (Exception e){
            e.printStackTrace();
            username=null;
        }
        return username;
    }

    /*从token中获取 荷载*/
    private Claims getClaimsFromToken(String token) {
       return Jwts.parser()
                .setSigningKey(secret)//向解析器中设置解析密钥,要跟生成该token的密钥保持一致
                .parseClaimsJws(token)
                .getBody();
    }

    /*验证token
    * 参数是 token
    * */
    public boolean validateToken(String token,User user){
        /*获取token中的用户名*/
        String userNameFromToken = getUserNameFromToken(token);
        /*判断 token 中的用户名 和当前登陆的用户名是否一致*/
        String username = user.getUsername();

        /*还要判断token是否过期*/
        return (userNameFromToken.equals(username)) && !isTokenExpire(token);
    }

    /*判断token是否过期*/
    public  boolean isTokenExpire(String token) {
        Date expireDate = getExpireDateFromToken(token);

        /*如果token中的过期时间在当前时间之前,说明已经过期*/
        return expireDate.before(new Date());
    }

    /*判断token是否可以被刷新*/
    public boolean canRefresh(String token){
        return !isTokenExpire(token);
    }

    /*从token中获取过期时间*/
    private Date getExpireDateFromToken(String token) {
        /*获取token荷载信息*/
        Claims claimsFromToken = getClaimsFromToken(token);
        return claimsFromToken.getExpiration();
    }

    /*刷新token*/
    public String refreshToken(String token){
        /*Claims 类 继承自Map接口
        * 刷新token,只改变token的创建时间,调用 generateToken(claims) 方法会重新获取当前系统时间+过期时间来刷新token过期时间
        * */
        Claims claims = getClaimsFromToken(token);
        claims.put(CLAIM_KEY_CREATED,new Date());
        return generateToken(claims);
    }
}
